package edu.zju.gis.cky.service.impl;

import edu.zju.gis.cky.cons.Constant;
import edu.zju.gis.cky.dao.MetaMapper;
import edu.zju.gis.cky.dao.StatisticMapper;
import edu.zju.gis.cky.entity.Meta;
import edu.zju.gis.cky.entity.StatArea;
import edu.zju.gis.cky.entity.dto.StatAreaByExtentDTO;
import edu.zju.gis.cky.entity.dto.StatisticsAreaByXzqhDTO;
import edu.zju.gis.cky.service.StatisticService;
import edu.zju.gis.cky.util.FileHelper;
import edu.zju.gis.cky.util.ShpHelper;
import org.gdal.ogr.Geometry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * @author jiarui
 * @version 1.0, 2022-01-12
 */
@Service
public class StatisticServiceImpl implements StatisticService {

    @Autowired
    private StatisticMapper statisticMapper;
    @Autowired
    private MetaMapper metaMapper;

    @Override
    public List<StatArea> statisticsAreaByExtent(StatAreaByExtentDTO dto) {
        //首先根据id获取到pg tableName
        Meta meta = metaMapper.selectByPrimaryKey(dto.getMetaId());
        String pgStorage = Constant.VECTOR_SCHEMA + "." + meta.getPgStorage();
        List<StatArea> statAreas = statisticMapper.statisticsAreaByExtent(pgStorage, dto.getExtent());
        HashMap<String, Double> hashMap = new HashMap<>();
        for (StatArea s : statAreas) {
            String key = s.getDlbm() + "_" + s.getDlmc();
            Double value = s.getArea();
            if (hashMap.containsKey(key)) {
                value += hashMap.get(key);
                hashMap.put(key, value);
            } else {
                hashMap.put(key, value);
            }
        }
        List<StatArea> result = new ArrayList<>();
        for (String key : hashMap.keySet()) {
            StatArea statArea = new StatArea();
            statArea.setDlbm(key.split("_")[0]);
            statArea.setDlmc(key.split("_")[1]);
            statArea.setArea(hashMap.get(key));
            result.add(statArea);
        }
        return result;
    }

    @Override
    public List<StatArea> statisticsAreaByXzqh(StatisticsAreaByXzqhDTO dto) {
        //首先根据id获取到pg tableName
        Meta meta = metaMapper.selectByPrimaryKey(dto.getMetaId());
        String pgStorage = Constant.VECTOR_SCHEMA + "." + meta.getPgStorage();
        String xzqh = dto.getXzqh();
        int xzqhIndex = xzqh.length();
        for (int i = 0; i < xzqh.length(); i++) {
            if (xzqh.charAt(xzqh.length() - i - 1) != '0') {
                xzqhIndex = xzqhIndex - i;
                break;
            }
        }
        List<StatArea> statAreas = statisticMapper.statisticsAreaByXzqh(pgStorage, xzqh.substring(0, xzqhIndex), xzqhIndex);
        HashMap<String, Double> hashMap = new HashMap<>();
        for (StatArea s : statAreas) {
            String key = s.getDlbm() + "_" + s.getDlmc();
            Double value = s.getArea();
            if (hashMap.containsKey(key)) {
                value += hashMap.get(key);
                hashMap.put(key, value);
            } else {
                hashMap.put(key, value);
            }
        }
        List<StatArea> result = new ArrayList<>();
        for (String key : hashMap.keySet()) {
            StatArea statArea = new StatArea();
            statArea.setDlbm(key.split("_")[0]);
            statArea.setDlmc(key.split("_")[1]);
            statArea.setArea(hashMap.get(key));
            result.add(statArea);
        }
        return result;
    }

    @Override
    public List<StatArea> statisticsAreaByShp(Long id, MultipartFile multipartFile) {
        if (multipartFile == null || multipartFile.isEmpty()) {
            throw new RuntimeException("Shp File is empty.");
        }
        String filename = multipartFile.getOriginalFilename();
        if (filename == null || !filename.contains("zip")) {
            throw new RuntimeException("Shp File should be zip.");
        }
        // 保存 shp 文件
        File file = new File(Constant.TEMP_DATA_PATH + "/" + filename);
        try {
            InputStream in = multipartFile.getInputStream();
            OutputStream out = new FileOutputStream(file);
            if (!FileHelper.streamWrite(in, out)) {
                throw new RuntimeException("Shp File transfer failed.");
            }
        } catch (IOException e) {
            throw new RuntimeException("Shp File transfer failed.");
        }
        // 解压 shp 文件
        try {
            ZipFile zipFile = new ZipFile(file);
            Enumeration<?> entries = zipFile.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();
                File targetFile = new File(Constant.TEMP_DATA_PATH + "/" + entry.getName());
                InputStream in = zipFile.getInputStream(entry);
                OutputStream out = new FileOutputStream(targetFile);
                if (!FileHelper.streamWrite(in, out)) {
                    throw new RuntimeException("Shp File transfer failed.");
                }
            }
        } catch (IOException exception) {
            exception.printStackTrace();
        }
        // 检查 shp 文件完整性
        File shpFile = new File(Constant.TEMP_DATA_PATH + "/" + filename.replace("zip", "shp"));
        File shxFile = new File(Constant.TEMP_DATA_PATH + "/" + filename.replace("zip", "shx"));
        File dbfFile = new File(Constant.TEMP_DATA_PATH + "/" + filename.replace("zip", "dbf"));
        if (!shpFile.exists() || !shxFile.exists() || !dbfFile.exists()) {
            throw new RuntimeException("Shp File is wrong.");
        }
        // 读取 shp 文件
        List<Geometry> geometries = ShpHelper.read(Constant.TEMP_DATA_PATH + "/" + filename.replace("zip", "shp"));
        return queryByGeometry(geometries, id);
    }

    private List<StatArea> queryByGeometry(List<Geometry> geometries, Long id) {
        HashMap<String, Double> hashMap = new HashMap<>();
        for (Geometry geometry : geometries) {
            String wkt = geometry.ExportToWkt();
            Meta meta = metaMapper.selectByPrimaryKey(id);
            String pgStorage = Constant.VECTOR_SCHEMA + "." + meta.getPgStorage();
            List<StatArea> statAreas = statisticMapper.statisticsAreaByExtent(pgStorage, wkt);
            for (StatArea s : statAreas) {
                String key = s.getDlbm() + "_" + s.getDlmc();
                Double value = s.getArea();
                if (hashMap.containsKey(key)) {
                    value += hashMap.get(key);
                    hashMap.put(key, value);
                } else {
                    hashMap.put(key, value);
                }
            }
        }
        List<StatArea> result = new ArrayList<>();
        for (String key : hashMap.keySet()) {
            StatArea statArea = new StatArea();
            statArea.setDlbm(key.split("_")[0]);
            statArea.setDlmc(key.split("_")[1]);
            statArea.setArea(hashMap.get(key));
            result.add(statArea);
        }
        return result;
    }
}
